/**
 * PANDA 3D SOFTWARE
 * Copyright (c) Carnegie Mellon University.  All rights reserved.
 *
 * All use of this software is subject to the terms of the revised BSD
 * license.  You should have received a copy of this license along
 * with this source code in a file named "LICENSE."
 *
 * @file pgSliderBar.I
 * @author masad
 * @date 2004-10-19
 */

/**
 * Sets the object which will be notified when the PGSliderBar changes.  Set
 * this to NULL to disable this effect.  The PGSliderBar does not retain
 * ownership of the pointer; it is your responsibility to ensure that the
 * notify object does not destruct.
 */
INLINE void PGSliderBar::
set_notify(PGSliderBarNotify *notify) {
  PGItem::set_notify(notify);
}

/**
 * Returns the object which will be notified when the PGSliderBar changes, if
 * any.  Returns NULL if there is no such object configured.
 */
INLINE PGSliderBarNotify *PGSliderBar::
get_notify() const {
  return (PGSliderBarNotify *)PGItem::get_notify();
}

/**
 * Specifies the axis of the slider bar's motion.  This should be only one of
 * four vectors: (1, 0, 0), (0, 0, 1), (-1, 0, 0), or (0, 0, -1).
 *
 * This specifies the vector in which the thumb moves when it is moving from
 * the minimum to the maximum value.
 *
 * The axis must be parallel to one of the screen axes, and it must be
 * normalized.  Hence, it may only be one of the above four possibilities;
 * anything else is an error and will result in indeterminate behavior.
 *
 * Normally, you should not try to set the axis directly.
 */
INLINE void PGSliderBar::
set_axis(const LVector3 &axis) {
  LightReMutexHolder holder(_lock);
  _axis = axis;
  _needs_remanage = true;
  _needs_recompute = true;
}

/**
 * Returns the axis of the slider bar's motion.  See set_axis().
 */
INLINE const LVector3 &PGSliderBar::
get_axis() const {
  LightReMutexHolder holder(_lock);
  return _axis;
}

/**
 * Sets the minimum and maxmimum value for the slider.
 */
INLINE void PGSliderBar::
set_range(PN_stdfloat min_value, PN_stdfloat max_value) {
  LightReMutexHolder holder(_lock);
  nassertv(min_value != max_value);
  _min_value = min_value;
  _max_value = max_value;
  _needs_recompute = true;

  if (has_notify()) {
    get_notify()->slider_bar_set_range(this);
  }
}

/**
 * Returns the value when the slider is all the way to the left.
 */
INLINE PN_stdfloat PGSliderBar::
get_min_value() const {
  LightReMutexHolder holder(_lock);
  return _min_value;
}

/**
 * Returns the value when the slider is all the way to the right.
 */
INLINE PN_stdfloat PGSliderBar::
get_max_value() const {
  LightReMutexHolder holder(_lock);
  return _max_value;
}

/**
 * Specifies the amount the slider will move when the user clicks on the left
 * or right buttons.
 */
INLINE void PGSliderBar::
set_scroll_size(PN_stdfloat value) {
  LightReMutexHolder holder(_lock);
  _scroll_value = value;
  _needs_recompute = true;
}

/**
 * Returns the value last set by set_scroll_size().
 */
INLINE PN_stdfloat PGSliderBar::
get_scroll_size() const {
  LightReMutexHolder holder(_lock);
  return _scroll_value;
}

/**
 * Specifies the amount of data contained in a single page.  This indicates
 * how much the thumb will jump when the trough is directly clicked; and if
 * resize_thumb is true, it also controls the visible size of the thumb
 * button.
 */
INLINE void PGSliderBar::
set_page_size(PN_stdfloat value) {
  LightReMutexHolder holder(_lock);
  _page_value = value;
  _needs_recompute = true;
}

/**
 * Returns the value last set by set_page_size().
 */
INLINE PN_stdfloat PGSliderBar::
get_page_size() const {
  LightReMutexHolder holder(_lock);
  return _page_value;
}

/**
 * Sets the current value of the slider programmatically.  This should range
 * between get_min_value() and get_max_value().
 */
INLINE void PGSliderBar::
set_value(PN_stdfloat value) {
  LightReMutexHolder holder(_lock);
  set_ratio((value - _min_value) / (_max_value - _min_value));
}

/**
 * Returns the current value of the slider.
 */
INLINE PN_stdfloat PGSliderBar::
get_value() const {
  LightReMutexHolder holder(_lock);
  return get_ratio() * (_max_value - _min_value) + _min_value;
}

/**
 * Sets the current value of the slider, expressed in the range 0 .. 1.
 */
INLINE void PGSliderBar::
set_ratio(PN_stdfloat ratio) {
  LightReMutexHolder holder(_lock);
  if (!is_button_down()) {
    internal_set_ratio(ratio);
  }
}

/**
 * Returns the current value of the slider, expressed in the range 0 .. 1.
 */
INLINE PN_stdfloat PGSliderBar::
get_ratio() const {
  LightReMutexHolder holder(_lock);
  return _ratio;
}

/**
 * Returns true if the user is currently holding down the mouse button to
 * manipulate the slider.  When true, calls to set_ratio() or set_value() will
 * have no effect.
 */
INLINE bool PGSliderBar::
is_button_down() const {
  LightReMutexHolder holder(_lock);
  return _dragging || _mouse_button_page ||
    (_scroll_button_held != nullptr);
}

/**
 * Sets the resize_thumb flag.  When this is true, the thumb button's frame
 * will be adjusted so that its width visually represents the page size.  When
 * this is false, the thumb button will be left alone.
 */
INLINE void PGSliderBar::
set_resize_thumb(bool resize_thumb) {
  LightReMutexHolder holder(_lock);
  _resize_thumb = resize_thumb;
  _needs_recompute = true;
}

/**
 * Returns the resize_thumb flag.  See set_resize_thumb().
 */
INLINE bool PGSliderBar::
get_resize_thumb() const {
  LightReMutexHolder holder(_lock);
  return _resize_thumb;
}

/**
 * Sets the manage_pieces flag.  When this is true, the sub-pieces of the
 * slider bar--that is, the thumb, and the left and right scroll buttons--are
 * automatically positioned and/or resized when the slider bar's overall frame
 * is changed.
 */
INLINE void PGSliderBar::
set_manage_pieces(bool manage_pieces) {
  LightReMutexHolder holder(_lock);
  _manage_pieces = manage_pieces;
  _needs_remanage = true;
  _needs_recompute = true;
}

/**
 * Returns the manage_pieces flag.  See set_manage_pieces().
 */
INLINE bool PGSliderBar::
get_manage_pieces() const {
  LightReMutexHolder holder(_lock);
  return _manage_pieces;
}

/**
 * Sets the PGButton object that will serve as the thumb for this slider.
 * This button visually represents the position of the slider, and can be
 * dragged left and right by the user.
 *
 * It is the responsibility of the caller to ensure that the button object is
 * parented to the PGSliderBar node.
 */
INLINE void PGSliderBar::
set_thumb_button(PGButton *thumb_button) {
  LightReMutexHolder holder(_lock);
  if (_thumb_button != nullptr) {
    _thumb_button->set_notify(nullptr);
  }
  _thumb_button = thumb_button;
  if (_thumb_button != nullptr) {
    _thumb_button->set_notify(this);
  }
  _needs_remanage = true;
  _needs_recompute = true;
}

/**
 * Removes the thumb button object from control of the frame.  It is your
 * responsibility to actually remove or hide the button itself.
 */
INLINE void PGSliderBar::
clear_thumb_button() {
  set_thumb_button(nullptr);
}

/**
 * Returns the PGButton that serves as the thumb for this slider, or NULL if
 * it is not set.
 */
INLINE PGButton *PGSliderBar::
get_thumb_button() const {
  LightReMutexHolder holder(_lock);
  return _thumb_button;
}

/**
 * Sets the PGButton object that will serve as the left scroll button for this
 * slider.  This button is optional; if present, the user can click on it to
 * move scroll_size units at a time to the left.
 *
 * It is the responsibility of the caller to ensure that the button object is
 * parented to the PGSliderBar node.
 */
INLINE void PGSliderBar::
set_left_button(PGButton *left_button) {
  LightReMutexHolder holder(_lock);
  if (_left_button != nullptr) {
    _left_button->set_notify(nullptr);
  }
  _left_button = left_button;
  if (_left_button != nullptr) {
    _left_button->set_notify(this);
  }
  _needs_remanage = true;
  _needs_recompute = true;
}

/**
 * Removes the left button object from control of the frame.  It is your
 * responsibility to actually remove or hide the button itself.
 */
INLINE void PGSliderBar::
clear_left_button() {
  set_left_button(nullptr);
}

/**
 * Returns the PGButton that serves as the left scroll button for this slider,
 * if any, or NULL if it is not set.
 */
INLINE PGButton *PGSliderBar::
get_left_button() const {
  LightReMutexHolder holder(_lock);
  return _left_button;
}

/**
 * Sets the PGButton object that will serve as the right scroll button for
 * this slider.  This button is optional; if present, the user can click on it
 * to move scroll_size units at a time to the right.
 *
 * It is the responsibility of the caller to ensure that the button object is
 * parented to the PGSliderBar node.
 */
INLINE void PGSliderBar::
set_right_button(PGButton *right_button) {
  LightReMutexHolder holder(_lock);
  if (_right_button != nullptr) {
    _right_button->set_notify(nullptr);
  }
  _right_button = right_button;
  if (_right_button != nullptr) {
    _right_button->set_notify(this);
  }
  _needs_remanage = true;
  _needs_recompute = true;
}

/**
 * Removes the right button object from control of the frame.  It is your
 * responsibility to actually remove or hide the button itself.
 */
INLINE void PGSliderBar::
clear_right_button() {
  set_right_button(nullptr);
}

/**
 * Returns the PGButton that serves as the right scroll button for this
 * slider, if any, or NULL if it is not set.
 */
INLINE PGButton *PGSliderBar::
get_right_button() const {
  LightReMutexHolder holder(_lock);
  return _right_button;
}

/**
 * Returns the prefix that is used to define the adjust event for all
 * PGSliderBars.  The adjust event is the concatenation of this string
 * followed by get_id().
 */
INLINE std::string PGSliderBar::
get_adjust_prefix() {
  return "adjust-";
}

/**
 * Returns the event name that will be thrown when the slider bar value is
 * adjusted by the user or programmatically.
 */
INLINE std::string PGSliderBar::
get_adjust_event() const {
  LightReMutexHolder holder(_lock);
  return get_adjust_prefix() + get_id();
}

/**
 * Sets the current value of the slider, expressed in the range 0 .. 1,
 * without checking whether the user is currently manipulating the slider.
 */
INLINE void PGSliderBar::
internal_set_ratio(PN_stdfloat ratio) {
  _ratio = (std::max)((std::min)(ratio, (PN_stdfloat)1.0), (PN_stdfloat)0.0);
  _needs_reposition = true;
  adjust();
}
